import { isObject, isArray } from "../utils";
import { arrayMethods } from "./array";

import Dep from './dep'
class Observer {
  constructor(val) {
    // 给对象和数组都添加dep属性
    this.dep = new Dep(); // {}.__ob__.dep  [].__ob__.dep
    //  val.__ob__ = this; TODO: 姜文写在这里了，如果val是对象则会死循环
    Object.defineProperty(val, '__ob__', {
      value: this,
      enumerable: false
    })
    if (isArray(val)) {
      // val.__ob__ = this; // 我写在这里了，val只能为数组，forEach不能遍历到 数组属性，所以没劫持，所以没报错
      // 1.如果为数组重写原型方法
      // 2.对数组中的值进行遍历，进行劫持
      val.__proto__ = arrayMethods;
      this.observeArray(val)

    } else {
      this.walk(val)
    }
  }
  // 监控数组中的值
  observeArray(data) {
    data.forEach(item => {
      observe(item)
    })
  }
  walk(data) {
    Object.keys(data).forEach(key => {
      defineReactive(data, key, data[key])
    })
  }
}
function dependArray(val) {
  for (let i = 0; i < val.length; i++) {
    let current = val[i];
    current.__ob__ && current.__ob__.dep.depend();
    if (Array.isArray(current)) {
      dependArray(current)
    }
  }
}

function defineReactive(obj, key, val) {
  // observe(val) 进行递归观测
  let childOb = observe(val) //如果childOb有值，val就是数组或对象
  let dep = new Dep(); // 给每个属性增加dep 闭包
  Object.defineProperty(obj, key, {
    get() {
      if (Dep.target) {
        dep.depend()
        if (childOb) {// 取属性的时候，会对对应的值（对象或数组本身）进行依赖(watcher)收集,这样当数组或对象变化时，才能触发对应watcher执行
          childOb.dep.depend() // 让数组和对象记住当前的watcher
          if (Array.isArray(val)) {
            // / 如果是数组，   姜文数组套数组，我一会看下数组套对象，
            // 试一下如果不加，当内部数组或对象更改时是否能更新
            dependArray(val)
          }
        }
      }

      return val
    },
    set(newVal) {
      if (val === newVal) return;
      observe(newVal) // 如果设置的是一个对象那么会再次进行劫持
      val = newVal
      dep.notify()
    }
  })
}
export function observe(val) {
  if (!isObject(val)) {
    return;
  }
  if (val.__ob__) {
    return; // 一个对象不需要重新被观测
  }

  return new Observer(val)
}
